package com.lagou.sqlSession;

import com.lagou.config.BoundSql;
import com.lagou.pojo.Configuration;
import com.lagou.pojo.MappedStatement;
import com.lagou.utils.BoundSqlUtil;
import com.lagou.utils.ClassUtil;
import com.lagou.utils.ParameterMapping;

import javax.sql.DataSource;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

/**
 * @author: jie.zhao
 * @create: 2021/02/02 14:40
 */
public class MapperInvocationHandler implements InvocationHandler {

    private Configuration configuration;

    private String className;

    public MapperInvocationHandler(Configuration configuration, String className) {
        this.configuration = configuration;
        this.className = className;
    }

    public Configuration getConfiguration() {
        return configuration;
    }

    public void setConfiguration(Configuration configuration) {
        this.configuration = configuration;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        String returnName = method.getReturnType().getName();

        Connection connection = configuration.getDataSource().getConnection();
        MappedStatement mappedStatement = configuration.getSqlMapperMap().get(className+"."+method.getName());
        String sql = mappedStatement.getSql();
        BoundSql boundSql = BoundSqlUtil.getBoundSql(sql);

        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());

        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
        for (int i = 0; i < parameterMappingList.size(); i++) {
            Class<?> parameterClass = ClassUtil.getClassType(mappedStatement.getParameterType());
            ParameterMapping parameterMapping = parameterMappingList.get(i);
            String content = parameterMapping.getContent();
            Field field = parameterClass.getDeclaredField(content);
            field.setAccessible(true);
            Object o = field.get(args[0]);
            preparedStatement.setObject(i + 1, o);
        }


        Class<?> resultTypeClass = ClassUtil.getClassType(mappedStatement.getResultType());
        if(returnName.equals("java.util.List")) {
            ResultSet resultSet = preparedStatement.executeQuery();
            List list = new ArrayList();
            while (resultSet.next()) {
                Object o = resultTypeClass.newInstance();
                ResultSetMetaData metaData = resultSet.getMetaData();
                for (int i = 1; i <= metaData.getColumnCount(); i++) {
                    String name = metaData.getColumnName(i);
                    Object value = resultSet.getObject(name);
                    PropertyDescriptor propertyDescriptor = new PropertyDescriptor(name, resultTypeClass);
                    Method writeMethod = propertyDescriptor.getWriteMethod();
                    writeMethod.invoke(o, value);
                }
                list.add(o);
            }
            return list;
        }
        if(sql.startsWith("select")) {
            ResultSet resultSet = preparedStatement.executeQuery();
            if (resultTypeClass != null) {
                Object o = resultTypeClass.newInstance();
                while (resultSet.next()) {

                    ResultSetMetaData metaData = resultSet.getMetaData();
                    for (int i = 1; i <= metaData.getColumnCount(); i++) {
                        String name = metaData.getColumnName(i);
                        Object value = resultSet.getObject(name);
                        PropertyDescriptor propertyDescriptor = new PropertyDescriptor(name, resultTypeClass);
                        Method writeMethod = propertyDescriptor.getWriteMethod();
                        writeMethod.invoke(o, value);
                    }
                }
                return o;
            }
        }
        boolean operateResult  = preparedStatement.execute();
        if(operateResult){
            return 1;
        }
        return 0;

    }
}
